<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        function runAsync(callback) {
            if (typeof queueMicrotask === 'function') {
                queueMicrotask(callback)
            } else if (typeof MutationObserver === 'function') {
                const obs = new MutationObserver(callback)
                obs.observe(document.body, { childList: true })
                document.body.textContent = '1'
            } else {
                setTimeout(callback, 100)
            }
        }

        function resolvePromise(promise2, x, resolve, reject) {
            if (x === promise2) {
                throw new TypeError('Chain cycle detected for promise #<Promise>')
            }
            if (x instanceof HMPromise) {
                x.then(resolve, reject)
            }
            else {
                resolve(x)
            }
        }
        const PENDING = 'pending'
        const FULFILLED = 'fulfilled'
        const REJECTED = 'rejected'
        class HMPromise {
            state = PENDING
            result = undefined
            #handlers = []
            constructor(executor) {
                const resolve = (result) => {
                    if (this.state === PENDING) {
                        this.state = FULFILLED
                        this.result = result
                        this.#handlers.forEach(handler => {
                            handler.onFulfilled(this.result)
                        })
                    }
                }
                const reject = (result) => {
                    if (this.state === PENDING) {
                        this.state = REJECTED
                        this.result = result
                        this.#handlers.forEach(handler => {
                            handler.onRejected(this.result)
                        })
                    }
                }

                try {
                    executor(resolve, reject)
                } catch (error) {
                    reject(error)
                }

            }
            then(onFulfilled, onRejected) {
                onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x
                onRejected = typeof onRejected === 'function' ? onRejected : x => { throw x }
                const promise2 = new HMPromise((resolve, reject) => {
                    if (this.state === FULFILLED) {
                        runAsync(() => {
                            try {
                                const x = onFulfilled(this.result)
                                //处理重复的引用
                                // if (x === promise2) {
                                //     throw new TypeError('Chain cycle detected for promise #<Promise>')
                                // }
                                // if (x instanceof HMPromise) {
                                //     x.then(resolve, reject)
                                // }
                                // else {
                                //     resolve(x)
                                // }
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    } else if (this.state === REJECTED) {
                        runAsync(() => {
                            try {
                                const x = onRejected(this.result)
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }

                        })
                    } else if (this.state === PENDING) {
                        this.#handlers.push({
                            onFulfilled: () => {
                                runAsync(() => {
                                    try {
                                        const x = onFulfilled(this.result)
                                        resolvePromise(promise2, x, resolve, reject)
                                    }
                                    catch (error) {
                                        reject(error)
                                    }
                                })
                            },
                            onRejected: () => {
                                runAsync(() => {
                                    try {
                                        const x = onRejected(this.result)
                                        resolvePromise(promise2, x, resolve, reject)
                                    }
                                    catch (error) {
                                        reject(error)
                                    }
                                })
                            }
                        })
                    }
                })
                return promise2
            }
            //实例方法
            catch(onRejected) {
                return this.then(undefined, onRejected)
            }
            finally(onFinally) {
                return this.then(onFinally, onFinally)
            }


            //静态方法
            static resolve(value) {
                if (value instanceof HMPromise) {
                    return value
                }
                //转为promise并返回(Fulfilled)
                return new HMPromise(resolve => {
                    resolve(value)
                })
            }
            static reject(reason) {
                //转为promise并返回(Rejected)
                return new HMPromise((undefined, reject) => {
                    reject(reason)
                })
            }
            static race(promises) {
                //返回一个promise
                return new HMPromise((resolve, reject) => {
                    //判断是否为数组
                    if (!Array.isArray(promises)) {
                        throw new TypeError('arguments must be an array')
                    }
                    //等待第一个被敲定
                    promises.forEach(p =>
                        HMPromise.resolve(p).then(
                            res => resolve(res),
                            err => reject(err)
                        )
                    )

                })
            }
            static all(promises) {

                return new HMPromise((resolve, reject) => {
                    //判断是否为数组
                    if (!Array.isArray(promises)) {
                        throw new TypeError('arguments must be an array')
                    }
                    //1.是数组后，如果是空数组，则直接兑现
                    if (promises.length === 0) {
                        resolve(promises)
                    }
                    ///2.数组非空，则开始处理全部兑现
                    const values = []
                    let count = 0
                    promises.forEach((p, index) => {
                        HMPromise.resolve(p).then(
                            res => {
                                values[index] = res
                                count++
                                if (count === promises.length) {
                                    resolve(values)
                                }
                            },
                            // 处理第一个拒绝
                            err => reject(err)
                        )
                    })


                })
            }
            static allSettled(promises) {

                return new HMPromise((resolve, reject) => {
                    //判断是否为数组
                    if (!Array.isArray(promises)) {
                        throw new TypeError('arguments must be an array')
                    }
                    //1.是数组后，如果是空数组，则直接兑现
                    if (promises.length === 0) {
                        resolve(promises)
                    }
                    ///2.数组非空，则等待全部兑现
                    const values = []
                    let count = 0
                    promises.forEach((p, index) => {
                        HMPromise.resolve(p).then(
                            res => {
                                values[index] = { status: FULFILLED, value: res }
                                count++
                                if (count === promises.length) {
                                    resolve(values)
                                }
                            },
                            err => {
                                values[index] = { status: REJECTED, reason: err }
                                count++
                                if (count === promises.length) {
                                    resolve(values)
                                }
                            }
                        )
                    })

                })
            }

            /*
           静态方法 any
            1.参数 promises数组
            2.结果
            2.1 获取第一个成功的原因
            2.2获取所有失败的原因
            2.3空数组直接拒绝
            2.4不穿数组，直接报错
            */
        }


        //测试
        const p1 = new HMPromise((resolve, reject) => {
            setTimeout(() => {
                resolve('p1')
            }, 10)
        })

        const p2 = new HMPromise((resolve, reject) => {
            setTimeout(() => {
                reject('p2')
            }, 20)
        })
        const p3 = new HMPromise((resolve, reject) => {
            setTimeout(() => {
                resolve('p3')
            }, 30)
        })
        const p4 = 'p4'
        const p5 = HMPromise.resolve('p5')

        // [p1, p2, p3, p4, p5]
        HMPromise.allSettled([p1, p2, p3, p4, p5]).then(res => {
            console.log(res)
        }).catch(err => {
            console.log(err)
        })
    </script>
</body>

</html>